/*************************************************************************
 *
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * Copyright 2000, 2010 Oracle and/or its affiliates.
 *
 * OpenOffice.org - a multi-platform office productivity suite
 *
 * This file is part of OpenOffice.org.
 *
 * OpenOffice.org is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License version 3
 * only, as published by the Free Software Foundation.
 *
 * OpenOffice.org is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License version 3 for more details
 * (a copy is included in the LICENSE file that accompanied this code).
 *
 * You should have received a copy of the GNU Lesser General Public License
 * version 3 along with OpenOffice.org.  If not, see
 * <http://www.openoffice.org/license.html>
 * for a copy of the LGPLv3 License.
 *
 ************************************************************************/

// MARKER(update_precomp.py): autogen include statement, do not remove
#include "precompiled_framework.hxx"

//_________________________________________________________________________________________________________________
//	my own includes
//_________________________________________________________________________________________________________________
#include "services/substitutepathvars.hxx"
#include <threadhelp/resetableguard.hxx>
#include <helper/networkdomain.hxx>
#include "services.h"

//_________________________________________________________________________________________________________________
//	interface includes
//_________________________________________________________________________________________________________________
#include <com/sun/star/beans/XPropertySet.hpp>

//_________________________________________________________________________________________________________________
//	includes of other projects
//_________________________________________________________________________________________________________________
#include <unotools/configitem.hxx>
#include <unotools/localfilehelper.hxx>
#include <unotools/configmgr.hxx>

#ifndef _UTL_BOOTSTRAP_HXX_
#include <unotools/bootstrap.hxx>
#endif
#include <osl/mutex.hxx>
#include <osl/file.hxx>
#include <osl/security.hxx>
#include <osl/socket.hxx>
#include <vos/process.hxx>
#include <i18npool/mslangid.hxx>
#include <tools/urlobj.hxx>
#include <tools/resmgr.hxx>
#include <tools/debug.hxx>
#include <tools/wldcrd.hxx>
#include <rtl/ustrbuf.hxx>
#include <rtl/bootstrap.hxx>

#include <comphelper/configurationhelper.hxx>

#include <string.h>

//_________________________________________________________________________________________________________________
//	Defines
//_________________________________________________________________________________________________________________
//

#define STRPOS_NOTFOUND						(sal_Int32)-1

#define ASCII_STR( val )					rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( val ))

#define SEARCHPATH_DELIMITER				';'

// Variable start/end characters
#define	SIGN_STARTVARIABLE					ASCII_STR("$(")
#define	SIGN_ENDVARIABLE					ASCII_STR(")")

// Length of SUBSTITUTE_... to replace it with real values.
#define	REPLACELENGTH_INST				7
#define	REPLACELENGTH_PROG				7
#define	REPLACELENGTH_USER				7
#define REPLACELENGTH_WORK				7
#define REPLACELENGTH_HOME				7
#define REPLACELENGTH_TEMP				7
#define REPLACELENGTH_PATH              7
#define	REPLACELENGTH_INSTPATH			11
#define	REPLACELENGTH_PROGPATH			11
#define	REPLACELENGTH_USERPATH			11
#define	REPLACELENGTH_INSTURL			10
#define	REPLACELENGTH_PROGURL			10
#define	REPLACELENGTH_USERURL			10
#define	REPLACELENGTH_PATH				7
#define	REPLACELENGTH_LANG				7
#define	REPLACELENGTH_LANGID			9
#define	REPLACELENGTH_VLANG				8
#define	REPLACELENGTH_WORKDIRURL		13
// --> PB 2004-10-27 #i32656# - new variable of hierachy service
#define REPLACELENGTH_BASEINSTURL       14
#define REPLACELENGTH_USERDATAURL       14
// <--

// Name of the pre defined path variables
#define VARIABLE_INST					"$(inst)"
#define VARIABLE_PROG					"$(prog)"
#define VARIABLE_USER					"$(user)"
#define VARIABLE_WORK					"$(work)"
#define VARIABLE_HOME					"$(home)"
#define VARIABLE_TEMP					"$(temp)"
#define VARIABLE_PATH					"$(path)"
#define VARIABLE_LANG					"$(lang)"
#define VARIABLE_LANGID					"$(langid)"
#define VARIABLE_VLANG					"$(vlang)"
#define VARIABLE_INSTPATH				"$(instpath)"
#define VARIABLE_PROGPATH				"$(progpath)"
#define VARIABLE_USERPATH				"$(userpath)"
#define VARIABLE_INSTURL				"$(insturl)"
#define VARIABLE_PROGURL				"$(progurl)"
#define VARIABLE_USERURL				"$(userurl)"
#define VARIABLE_WORKDIRURL				"$(workdirurl)"
// --> PB 2004-10-27 #i32656# - new variable of hierachy service
#define VARIABLE_BASEINSTURL            "$(baseinsturl)"
#define VARIABLE_USERDATAURL            "$(userdataurl)"
// <--

using namespace com::sun::star::uno;
using namespace com::sun::star::beans;
using namespace com::sun::star::util;
using namespace com::sun::star::lang;
using namespace com::sun::star::container;

//_________________________________________________________________________________________________________________
//	Namespace
//_________________________________________________________________________________________________________________
//

namespace framework
{

struct FixedVariable
{
	const char*		pVarName;
	PreDefVariable	nEnumValue;
	int				nStrLen;
};

struct TableEntry
{
	const char* pOSString;
	int			nStrLen;
};

// Table with valid operating system strings
// Name of the os as char* and the length
// of the string
static TableEntry aOSTable[OS_COUNT] =
{
	{ "WINDOWS"	,	7	},
	{ "UNIX"	,	4	},
	{ "SOLARIS"	,	7	},
	{ "LINUX"	,	5	},
	{ ""		,	0	}	// unknown
};

// Table with valid environment variables
// Name of the environment type as a char* and
// the length of the string.
static TableEntry aEnvTable[ET_COUNT] =
{
	{ "HOST"		,	4	},
	{ "YPDOMAIN"	,	8	},
	{ "DNSDOMAIN"	,	9	},
	{ "NTDOMAIN"	,	8	},
	{ "OS"			,	2	},
	{ ""			,	0	} // unknown
};

// Priority table for the environment types. Lower numbers define
// a higher priority. Equal numbers has the same priority that means
// that the first match wins!!
static sal_Int16 aEnvPrioTable[ET_COUNT] =
{
	1,	// ET_HOST
	2,	// ET_IPDOMAIN
	2,	// ET_DNSDOMAIN
	2,	// ET_NTDOMAIN
	3,	// ET_OS
	99,	// ET_UNKNOWN
};

// Table with all fixed/predefined variables supported.
static FixedVariable aFixedVarTable[] =
{
	{ VARIABLE_INST,		PREDEFVAR_INST,			REPLACELENGTH_INST			},
	{ VARIABLE_PROG,		PREDEFVAR_PROG,			REPLACELENGTH_PROG			},
	{ VARIABLE_USER,		PREDEFVAR_USER,			REPLACELENGTH_USER			},
	{ VARIABLE_WORK,		PREDEFVAR_WORK,			REPLACELENGTH_WORK			},	// Special variable (transient)!
	{ VARIABLE_HOME,		PREDEFVAR_HOME,			REPLACELENGTH_HOME			},
	{ VARIABLE_TEMP,		PREDEFVAR_TEMP,			REPLACELENGTH_TEMP			},
	{ VARIABLE_PATH,        PREDEFVAR_PATH,         REPLACELENGTH_PATH          },
	{ VARIABLE_LANG,		PREDEFVAR_LANG,			REPLACELENGTH_LANG			},
	{ VARIABLE_LANGID,		PREDEFVAR_LANGID,		REPLACELENGTH_LANGID		},
	{ VARIABLE_VLANG,		PREDEFVAR_VLANG,		REPLACELENGTH_VLANG			},
	{ VARIABLE_INSTPATH,	PREDEFVAR_INSTPATH,		REPLACELENGTH_INSTPATH		},
	{ VARIABLE_PROGPATH,	PREDEFVAR_PROGPATH,		REPLACELENGTH_PROGPATH		},
	{ VARIABLE_USERPATH,	PREDEFVAR_USERPATH,		REPLACELENGTH_USERPATH		},
	{ VARIABLE_INSTURL,		PREDEFVAR_INSTURL,		REPLACELENGTH_INSTURL		},
	{ VARIABLE_PROGURL,		PREDEFVAR_PROGURL,		REPLACELENGTH_PROGURL		},
	{ VARIABLE_USERURL,		PREDEFVAR_USERURL,		REPLACELENGTH_USERURL		},
    { VARIABLE_WORKDIRURL,  PREDEFVAR_WORKDIRURL,   REPLACELENGTH_WORKDIRURL    },  // Special variable (transient) and don't use for resubstitution!
    // --> PB 2004-10-27 #i32656# - new variable of hierachy service
    { VARIABLE_BASEINSTURL, PREDEFVAR_BASEINSTURL,  REPLACELENGTH_BASEINSTURL   },
    { VARIABLE_USERDATAURL, PREDEFVAR_USERDATAURL,  REPLACELENGTH_USERDATAURL   },
    // <--
    { "$(brandbaseurl)", PREDEFVAR_BRANDBASEURL,
      RTL_CONSTASCII_LENGTH("$(brandbaseurl)") }
};

//_________________________________________________________________________________________________________________
//	Implementation helper classes
//_________________________________________________________________________________________________________________
//

OperatingSystem SubstitutePathVariables_Impl::GetOperatingSystemFromString( const rtl::OUString& aOSString )
{
	for ( int i = 0; i < OS_COUNT; i++ )
	{
		if ( aOSString.equalsIgnoreAsciiCaseAsciiL( aOSTable[i].pOSString, aOSTable[i].nStrLen ))
			return (OperatingSystem)i;
	}

	return OS_UNKNOWN;
}

EnvironmentType SubstitutePathVariables_Impl::GetEnvTypeFromString( const rtl::OUString& aEnvTypeString )
{
	for ( int i = 0; i < ET_COUNT; i++ )
	{
		if ( aEnvTypeString.equalsIgnoreAsciiCaseAsciiL( aEnvTable[i].pOSString, aEnvTable[i].nStrLen ))
			return (EnvironmentType)i;
	}

	return ET_UNKNOWN;
}

SubstitutePathVariables_Impl::SubstitutePathVariables_Impl( const Link& aNotifyLink ) :
	utl::ConfigItem( ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "Office.Substitution" ))),
	m_bYPDomainRetrieved( sal_False ),
	m_bDNSDomainRetrieved( sal_False ),
	m_bNTDomainRetrieved( sal_False ),
	m_bHostRetrieved( sal_False ),
	m_bOSRetrieved( sal_False ),
	m_aListenerNotify( aNotifyLink ),
	m_aSharePointsNodeName( rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "SharePoints" ))),
	m_aDirPropertyName( rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "/Directory" ))),
	m_aEnvPropertyName( rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "/Environment" ))),
	m_aLevelSep( rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "/" )))
{
	// Enable notification mechanism
	// We need it to get information about changes outside these class on our configuration branch
    Sequence< rtl::OUString > aNotifySeq( 1 );
    aNotifySeq[0] = rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "SharePoints" ));
    EnableNotification( aNotifySeq, sal_True );
}

SubstitutePathVariables_Impl::~SubstitutePathVariables_Impl()
{
}

void SubstitutePathVariables_Impl::GetSharePointsRules( SubstituteVariables& aSubstVarMap )
{
	Sequence< rtl::OUString > aSharePointNames;
	ReadSharePointsFromConfiguration( aSharePointNames );

	if ( aSharePointNames.getLength() > 0 )
	{
		sal_Int32 nSharePoints = 0;

		// Read SharePoints container from configuration
		while ( nSharePoints < aSharePointNames.getLength() )
		{
			rtl::OUString aSharePointNodeName( m_aSharePointsNodeName );
			aSharePointNodeName += rtl::OUString::createFromAscii( "/" );
			aSharePointNodeName += aSharePointNames[ nSharePoints ];

			SubstituteRuleVector aRuleSet;
			ReadSharePointRuleSetFromConfiguration( aSharePointNames[ nSharePoints ], aSharePointNodeName, aRuleSet );
			if ( !aRuleSet.empty() )
			{
				// We have at minimum one rule. Filter the correct rule out of the rule set
				// and put into our SubstituteVariable map
				SubstituteRule aActiveRule;
				if ( FilterRuleSet( aRuleSet, aActiveRule ))
				{
					// We have found an active rule
					aActiveRule.aSubstVariable = aSharePointNames[ nSharePoints ];
					aSubstVarMap.insert( SubstituteVariables::value_type(
						aActiveRule.aSubstVariable, aActiveRule ));
				}
			}

			++nSharePoints;
		}
	}
}

void SubstitutePathVariables_Impl::Notify( const com::sun::star::uno::Sequence< rtl::OUString >& /*aPropertyNames*/ )
{
	// NOT implemented yet!
}

//_________________________________________________________________________________________________________________
//	private methods
//_________________________________________________________________________________________________________________
//

OperatingSystem SubstitutePathVariables_Impl::GetOperatingSystem()
{
	if ( !m_bOSRetrieved )
	{
#ifdef SOLARIS
		m_eOSType = OS_SOLARIS;
#elif defined LINUX
		m_eOSType = OS_LINUX;
#elif defined WIN32
		m_eOSType = OS_WINDOWS;
#elif defined UNIX
		m_eOSType = OS_UNIX;
#else
		m_eOSType = OS_UNKNOWN;
#endif
		m_bOSRetrieved = sal_True;
	}

	return m_eOSType;
}

const rtl::OUString& SubstitutePathVariables_Impl::GetYPDomainName()
{
	if ( !m_bYPDomainRetrieved )
	{
		m_aYPDomain = NetworkDomain::GetYPDomainName().toAsciiLowerCase();
		m_bYPDomainRetrieved = sal_True;
	}

	return m_aYPDomain;
}

const rtl::OUString& SubstitutePathVariables_Impl::GetDNSDomainName()
{
	if ( !m_bDNSDomainRetrieved )
	{
		rtl::OUString	aTemp;
		osl::SocketAddr	aSockAddr;
		oslSocketResult aResult;

		rtl::OUString aHostName = GetHostName();
		osl::SocketAddr::resolveHostname( aHostName, aSockAddr );
		aTemp = aSockAddr.getHostname( &aResult );

		// DNS domain name begins after the first "."
		sal_Int32 nIndex = aTemp.indexOf( '.' );
		if ( nIndex >= 0 && aTemp.getLength() > nIndex+1 )
			m_aDNSDomain = aTemp.copy( nIndex+1 ).toAsciiLowerCase();
		else
			m_aDNSDomain = rtl::OUString();

		m_bDNSDomainRetrieved = sal_True;
	}

	return m_aDNSDomain;
}

const rtl::OUString& SubstitutePathVariables_Impl::GetNTDomainName()
{
	if ( !m_bNTDomainRetrieved )
	{
		m_aNTDomain = NetworkDomain::GetNTDomainName().toAsciiLowerCase();
		m_bNTDomainRetrieved = sal_True;
	}

	return m_aNTDomain;
}

const rtl::OUString& SubstitutePathVariables_Impl::GetHostName()
{
	if ( !m_bHostRetrieved )
	{
		rtl::OUString	aHostName;
		oslSocketResult aSocketResult;

		m_aHost = osl::SocketAddr::getLocalHostname( &aSocketResult ).toAsciiLowerCase();
	}

	return m_aHost;
}

sal_Bool SubstitutePathVariables_Impl::FilterRuleSet( const SubstituteRuleVector& aRuleSet, SubstituteRule& aActiveRule )
{
	sal_Bool bResult = sal_False;

	if ( !aRuleSet.empty() )
	{
		sal_Int16	nPrioCurrentRule = aEnvPrioTable[ ET_UNKNOWN ];
        const sal_uInt32 nCount = aRuleSet.size();
		for ( sal_uInt32 nIndex = 0; nIndex < nCount; nIndex++ )
		{
			const SubstituteRule& aRule = aRuleSet[nIndex];
			EnvironmentType eEnvType	= aRule.aEnvType;

			// Check if environment type has a higher priority than current one!
			if ( nPrioCurrentRule > aEnvPrioTable[eEnvType] )
			{
				switch ( eEnvType )
				{
					case ET_HOST:
					{
						rtl::OUString aHost = GetHostName();
						rtl::OUString aHostStr;
						aRule.aEnvValue >>= aHostStr;
						aHostStr = aHostStr.toAsciiLowerCase();

						// Pattern match if domain environment match
                                    WildCard aPattern(aHostStr);
                                    sal_Bool bMatch = aPattern.Matches(aHost);
						if ( bMatch )
						{
							aActiveRule			= aRule;
							bResult				= sal_True;
							nPrioCurrentRule	= aEnvPrioTable[eEnvType];
						}
					}
					break;

					case ET_YPDOMAIN:
					case ET_DNSDOMAIN:
					case ET_NTDOMAIN:
					{
						rtl::OUString	aDomain;
						rtl::OUString	aDomainStr;
						aRule.aEnvValue >>= aDomainStr;
						aDomainStr = aDomainStr.toAsciiLowerCase();

						// Retrieve the correct domain value
						if ( eEnvType == ET_YPDOMAIN )
							aDomain = GetYPDomainName();
						else if ( eEnvType == ET_DNSDOMAIN )
							aDomain = GetDNSDomainName();
						else
							aDomain = GetNTDomainName();

						// Pattern match if domain environment match
                                    WildCard aPattern(aDomainStr);
                                    sal_Bool bMatch = aPattern.Matches(aDomain);
						if ( bMatch )
						{
							aActiveRule			= aRule;
							bResult				= sal_True;
							nPrioCurrentRule	= aEnvPrioTable[eEnvType];
						}
					}
					break;

					case ET_OS:
					{
						// No pattern matching for OS type
						OperatingSystem	eOSType = GetOperatingSystem();

						sal_Int16 nValue = 0;
						aRule.aEnvValue >>= nValue;

						sal_Bool		bUnix = ( eOSType == OS_LINUX ) || ( eOSType == OS_SOLARIS );
						OperatingSystem	eRuleOSType = (OperatingSystem)nValue;

						// Match if OS identical or rule is set to UNIX and OS is LINUX/SOLARIS!
						if (( eRuleOSType == eOSType ) || ( eRuleOSType == OS_UNIX && bUnix ))
						{
							aActiveRule			= aRule;
							bResult				= sal_True;
							nPrioCurrentRule	= aEnvPrioTable[eEnvType];
						}
					}
					break;

                              case ET_UNKNOWN: // nothing to do
                                  break;

                              default:
                                  break;
				}
			}
		}
	}

	return bResult;
}

void SubstitutePathVariables_Impl::ReadSharePointsFromConfiguration( Sequence< rtl::OUString >& aSharePointsSeq )
{
    //returns all the names of all share point nodes
    aSharePointsSeq = GetNodeNames( m_aSharePointsNodeName );
}

void SubstitutePathVariables_Impl::ReadSharePointRuleSetFromConfiguration(
	const rtl::OUString& aSharePointName,
	const rtl::OUString& aSharePointNodeName,
	SubstituteRuleVector& rRuleSet )
{
	Sequence< rtl::OUString > aSharePointMappingsNodeNames = GetNodeNames( aSharePointNodeName, utl::CONFIG_NAME_LOCAL_PATH );

	sal_Int32 nSharePointMapping = 0;
	while ( nSharePointMapping < aSharePointMappingsNodeNames.getLength() )
	{
		rtl::OUString aSharePointMapping( aSharePointNodeName );
		aSharePointMapping += m_aLevelSep;
		aSharePointMapping += aSharePointMappingsNodeNames[ nSharePointMapping ];

		// Read SharePointMapping
		rtl::OUString aDirValue;
		rtl::OUString aDirProperty( aSharePointMapping );
		aDirProperty += m_aDirPropertyName;

		// Read only the directory property
		Sequence< rtl::OUString > aDirPropertySeq( 1 );
		aDirPropertySeq[0] = aDirProperty;

		Sequence< Any > aValueSeq = GetProperties( aDirPropertySeq );
		if ( aValueSeq.getLength() == 1 )
			aValueSeq[0] >>= aDirValue;

		// Read the environment setting
		rtl::OUString aEnvUsed;
		rtl::OUString aEnvProperty( aSharePointMapping );
		aEnvProperty += m_aEnvPropertyName;
		Sequence< rtl::OUString > aEnvironmentVariable = GetNodeNames( aEnvProperty );

		// Filter the property which has a value set
		Sequence< rtl::OUString > aEnvUsedPropertySeq( aEnvironmentVariable.getLength() );

		rtl::OUString aEnvUsePropNameTemplate( aEnvProperty );
		aEnvUsePropNameTemplate += m_aLevelSep;

		for ( sal_Int32 nProperty = 0; nProperty < aEnvironmentVariable.getLength(); nProperty++ )
			aEnvUsedPropertySeq[nProperty] = rtl::OUString( aEnvUsePropNameTemplate + aEnvironmentVariable[nProperty] );

		Sequence< Any > aEnvUsedValueSeq;
		aEnvUsedValueSeq = GetProperties( aEnvUsedPropertySeq );

		rtl::OUString aEnvUsedValue;
		for ( sal_Int32 nIndex = 0; nIndex < aEnvironmentVariable.getLength(); nIndex++ )
		{
			if ( aEnvUsedValueSeq[nIndex] >>= aEnvUsedValue )
			{
				aEnvUsed = aEnvironmentVariable[nIndex];
				break;
			}
		}

		// Decode the environment and optional the operatng system settings
		Any				aEnvValue;
		EnvironmentType eEnvType = GetEnvTypeFromString( aEnvUsed );
		if ( eEnvType == ET_OS )
		{
			OperatingSystem eOSType = GetOperatingSystemFromString( aEnvUsedValue );
			aEnvValue <<= (sal_Int16)eOSType;
		}
		else
			aEnvValue <<= aEnvUsedValue;

		// Create rule struct and push it into the rule set
		SubstituteRule aRule( aSharePointName, aDirValue, aEnvValue, eEnvType );
		rRuleSet.push_back( aRule );

		++nSharePointMapping;
	}
}

//*****************************************************************************************************************
//	XInterface, XTypeProvider, XServiceInfo
//*****************************************************************************************************************
DEFINE_XSERVICEINFO_ONEINSTANCESERVICE  (   SubstitutePathVariables						,
                                            ::cppu::OWeakObject							,
                                            SERVICENAME_SUBSTITUTEPATHVARIABLES			,
											IMPLEMENTATIONNAME_SUBSTITUTEPATHVARIABLES
										)

DEFINE_INIT_SERVICE                     (   SubstitutePathVariables, {} )


SubstitutePathVariables::SubstitutePathVariables( const Reference< XMultiServiceFactory >& xServiceManager ) :
	ThreadHelpBase(),
	m_aVarStart( SIGN_STARTVARIABLE ),
	m_aVarEnd( SIGN_ENDVARIABLE ),
	m_aImpl( LINK( this, SubstitutePathVariables, implts_ConfigurationNotify )),
	m_xServiceManager( xServiceManager )
{
    RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "framework", "Ocke.Janssen@sun.com", "SubstitutePathVariables::SubstitutePathVariables" );
	int i;

	SetPredefinedPathVariables( m_aPreDefVars );
	m_aImpl.GetSharePointsRules( m_aSubstVarMap );

	// Init the predefined/fixed variable to index hash map
	for ( i = 0; i < PREDEFVAR_COUNT; i++ )
	{
		// Store variable name into struct of predefined/fixed variables
		m_aPreDefVars.m_FixedVarNames[i] = rtl::OUString::createFromAscii( aFixedVarTable[i].pVarName );

		// Create hash map entry
		m_aPreDefVarMap.insert( VarNameToIndexMap::value_type(
			m_aPreDefVars.m_FixedVarNames[i], aFixedVarTable[i].nEnumValue ) );
	}

	// Sort predefined/fixed variable to path length
	for ( i = 0; i < PREDEFVAR_COUNT; i++ )
	{
		if (( i != PREDEFVAR_WORKDIRURL ) &&
		    ( i != PREDEFVAR_PATH ))
		{
			// Special path variables, don't include into automatic resubstituion search!
			// $(workdirurl) is not allowed to resubstitute! This variable is the value of path settings entry
			// and it could be possible that it will be resubstituted by itself!!
			// Example: WORK_PATH=c:\test, $(workdirurl)=WORK_PATH => WORK_PATH=$(workdirurl) and this cannot be substituted!
			ReSubstFixedVarOrder aFixedVar;
			aFixedVar.eVariable			= aFixedVarTable[i].nEnumValue;
			aFixedVar.nVarValueLength	= m_aPreDefVars.m_FixedVar[(sal_Int32)aFixedVar.eVariable].getLength();
			m_aReSubstFixedVarOrder.push_back( aFixedVar );
		}
	}
	m_aReSubstFixedVarOrder.sort();

	// Sort user variables to path length
	SubstituteVariables::const_iterator pIter;
	for ( pIter = m_aSubstVarMap.begin(); pIter != m_aSubstVarMap.end(); pIter++ )
	{
		ReSubstUserVarOrder aUserOrderVar;
		rtl::OUStringBuffer aStrBuffer( pIter->second.aSubstVariable.getLength() );
		aStrBuffer.append( m_aVarStart );
		aStrBuffer.append( pIter->second.aSubstVariable );
		aStrBuffer.append( m_aVarEnd );
		aUserOrderVar.aVarName			= aStrBuffer.makeStringAndClear();
		aUserOrderVar.nVarValueLength	= pIter->second.aSubstVariable.getLength();
		m_aReSubstUserVarOrder.push_back( aUserOrderVar );
	}
	m_aReSubstUserVarOrder.sort();
}

SubstitutePathVariables::~SubstitutePathVariables()
{
}

// XStringSubstitution
rtl::OUString SAL_CALL SubstitutePathVariables::substituteVariables( const ::rtl::OUString& aText, sal_Bool bSubstRequired )
throw ( NoSuchElementException, RuntimeException )
{
    RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "framework", "Ocke.Janssen@sun.com", "SubstitutePathVariables::substituteVariables" );
    ResetableGuard aLock( m_aLock );
	return impl_substituteVariable( aText, bSubstRequired );
}

rtl::OUString SAL_CALL SubstitutePathVariables::reSubstituteVariables( const ::rtl::OUString& aText )
throw ( RuntimeException )
{
    RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "framework", "Ocke.Janssen@sun.com", "SubstitutePathVariables::reSubstituteVariables" );
    ResetableGuard aLock( m_aLock );
	return impl_reSubstituteVariables( aText );
}

rtl::OUString SAL_CALL SubstitutePathVariables::getSubstituteVariableValue( const ::rtl::OUString& aVariable )
throw ( NoSuchElementException, RuntimeException )
{
    RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "framework", "Ocke.Janssen@sun.com", "SubstitutePathVariables::getSubstituteVariableValue" );
    ResetableGuard aLock( m_aLock );
	return impl_getSubstituteVariableValue( aVariable );
}

//_________________________________________________________________________________________________________________
//	protected methods
//_________________________________________________________________________________________________________________
//

IMPL_LINK( SubstitutePathVariables, implts_ConfigurationNotify, SubstitutePathNotify*, EMPTYARG )
{
    /* SAFE AREA ----------------------------------------------------------------------------------------------- */
    ResetableGuard aLock( m_aLock );

	return 0;
}

rtl::OUString SubstitutePathVariables::ConvertOSLtoUCBURL( const rtl::OUString& aOSLCompliantURL ) const
{
    RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "framework", "Ocke.Janssen@sun.com", "SubstitutePathVariables::ConvertOSLtoUCBURL" );
	String			aResult;
	rtl::OUString	aTemp;

	osl::FileBase::getSystemPathFromFileURL( aOSLCompliantURL, aTemp );
	utl::LocalFileHelper::ConvertPhysicalNameToURL( aTemp, aResult );

	// Not all OSL URL's can be mapped to UCB URL's!
	if ( aResult.Len() == 0 )
		return aOSLCompliantURL;
	else
		return rtl::OUString( aResult );
}

rtl::OUString SubstitutePathVariables::GetWorkPath() const
{
    RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "framework", "Ocke.Janssen@sun.com", "SubstitutePathVariables::GetWorkPath" );
	rtl::OUString aWorkPath;
    ::comphelper::ConfigurationHelper::readDirectKey(
                            m_xServiceManager,
                            ::rtl::OUString::createFromAscii("org.openoffice.Office.Paths"),
                            ::rtl::OUString::createFromAscii("Paths/Work"),
                            ::rtl::OUString::createFromAscii("WritePath"),
                            ::comphelper::ConfigurationHelper::E_READONLY) >>= aWorkPath;
    return aWorkPath;
}

rtl::OUString SubstitutePathVariables::GetWorkVariableValue() const
{
    RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "framework", "Ocke.Janssen@sun.com", "SubstitutePathVariables::GetWorkVariableValue" );
    ::rtl::OUString aWorkPath;
    ::comphelper::ConfigurationHelper::readDirectKey(
                            m_xServiceManager,
                            ::rtl::OUString::createFromAscii("org.openoffice.Office.Paths"),
                            ::rtl::OUString::createFromAscii("Variables"),
                            ::rtl::OUString::createFromAscii("Work"),
                            ::comphelper::ConfigurationHelper::E_READONLY) >>= aWorkPath;
	
    // fallback to $HOME in  case platform dependend config layer does not return
    // an usuable work dir value.
    if (aWorkPath.getLength() < 1)
    {
    	osl::Security aSecurity;
    	aSecurity.getHomeDir( aWorkPath );
    }
	return ConvertOSLtoUCBURL( aWorkPath );
}

rtl::OUString SubstitutePathVariables::GetHomeVariableValue() const
{
    RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "framework", "Ocke.Janssen@sun.com", "SubstitutePathVariables::GetHomeVariableValue" );
	osl::Security	aSecurity;
	rtl::OUString	aHomePath;

	aSecurity.getHomeDir( aHomePath );
	return ConvertOSLtoUCBURL( aHomePath );
}

rtl::OUString SubstitutePathVariables::GetPathVariableValue() const
{
    RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "framework", "Ocke.Janssen@sun.com", "SubstitutePathVariables::GetPathVariableValue" );
    const int PATH_EXTEND_FACTOR = 120;

	rtl::OUString aRetStr;
    const char*   pEnv = getenv( "PATH" );

	if ( pEnv )
	{
		rtl::OUString       aTmp;
		rtl::OUString       aPathList( pEnv, strlen( pEnv ), gsl_getSystemTextEncoding() );
		rtl::OUStringBuffer aPathStrBuffer( aPathList.getLength() * PATH_EXTEND_FACTOR / 100 );

		sal_Bool  bAppendSep = sal_False;
		sal_Int32 nToken = 0;
		do
		{
			::rtl::OUString sToken = aPathList.getToken(0, SAL_PATHSEPARATOR, nToken);
			if (sToken.getLength())
			{
				osl::FileBase::getFileURLFromSystemPath( sToken, aTmp );
				if ( bAppendSep )
					aPathStrBuffer.appendAscii( ";" ); // Office uses ';' as path separator
				aPathStrBuffer.append( aTmp );
				bAppendSep = sal_True;
			}
		}
		while(nToken>=0);

		aRetStr = aPathStrBuffer.makeStringAndClear();
	}

	return aRetStr;
}

rtl::OUString SubstitutePathVariables::impl_substituteVariable( const ::rtl::OUString& rText, sal_Bool bSubstRequired )
throw ( NoSuchElementException, RuntimeException )
{
    RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "framework", "Ocke.Janssen@sun.com", "SubstitutePathVariables::impl_substituteVariable" );
	// This is maximal recursive depth supported!
	const sal_Int32 nMaxRecursiveDepth = 8;

	rtl::OUString	aWorkText = rText;
	rtl::OUString	aResult;

	// Use vector with strings to detect endless recursions!
	std::vector< rtl::OUString > aEndlessRecursiveDetector;

	// Search for first occure of "$(...".
	sal_Int32	nDepth = 0;
	sal_Int32	bSubstitutionCompleted = sal_False;
	sal_Int32	nPosition	= aWorkText.indexOf( m_aVarStart );	// = first position of "$(" in string
	sal_Int32	nLength = 0; // = count of letters from "$(" to ")" in string
	sal_Bool	bVarNotSubstituted = sal_False;

	// Have we found any variable like "$(...)"?
	if ( nPosition != STRPOS_NOTFOUND )
	{
		// Yes; Get length of found variable.
		// If no ")" was found - nLength is set to 0 by default! see before.
		sal_Int32 nEndPosition = aWorkText.indexOf( m_aVarEnd, nPosition );
		if ( nEndPosition != STRPOS_NOTFOUND )
			nLength = nEndPosition - nPosition + 1;
	}

	// Is there something to replace ?
	sal_Bool bWorkRetrieved			= sal_False;
	sal_Bool bWorkDirURLRetrieved	= sal_False;
	while ( !bSubstitutionCompleted && nDepth < nMaxRecursiveDepth )
	{
		while ( ( nPosition != STRPOS_NOTFOUND ) && ( nLength > 3 ) ) // "$(" ")"
		{
			// YES; Get the next variable for replace.
			sal_Int32		nReplaceLength	= 0;
			rtl::OUString	aReplacement;
			rtl::OUString	aSubString		= aWorkText.copy( nPosition, nLength );
			rtl::OUString	aSubVarString;

			// Path variables are not case sensitive!
			aSubVarString = aSubString.toAsciiLowerCase();
			VarNameToIndexMap::const_iterator pNTOIIter = m_aPreDefVarMap.find( aSubVarString );
			if ( pNTOIIter != m_aPreDefVarMap.end() )
			{
				// Fixed/Predefined variable found
				PreDefVariable nIndex = (PreDefVariable)pNTOIIter->second;

				// Determine variable value and length from array/table
				if ( nIndex == PREDEFVAR_WORK && !bWorkRetrieved )
				{
					// Transient value, retrieve it again
					m_aPreDefVars.m_FixedVar[ (PreDefVariable)nIndex ] = GetWorkVariableValue();
					bWorkRetrieved = sal_True;
				}
				else if ( nIndex == PREDEFVAR_WORKDIRURL && !bWorkDirURLRetrieved )
				{
					// Transient value, retrieve it again
					m_aPreDefVars.m_FixedVar[ (PreDefVariable)nIndex ] = GetWorkPath();
					bWorkDirURLRetrieved = sal_True;
				}

				aReplacement = m_aPreDefVars.m_FixedVar[ (PreDefVariable)nIndex ];
				nReplaceLength = nLength;
			}
			else
			{
				// Extract the variable name and try to find in the user defined variable set
				rtl::OUString aVarName = aSubString.copy( 2, nLength-3 );
				SubstituteVariables::const_iterator pIter = m_aSubstVarMap.find( aVarName );
				if ( pIter != m_aSubstVarMap.end() )
				{
					// found!
					aReplacement = pIter->second.aSubstValue;
					nReplaceLength = nLength;
				}
			}

			// Have we found something to replace?
			if ( nReplaceLength > 0 )
			{
				// Yes ... then do it.
				aWorkText = aWorkText.replaceAt( nPosition, nReplaceLength, aReplacement );
			}
			else
			{
				// Variable not known
				bVarNotSubstituted = sal_False;
				nPosition += nLength;
			}

			// Step after replaced text! If no text was replaced (unknown variable!),
			// length of aReplacement is 0 ... and we don't step then.
			nPosition += aReplacement.getLength();

			// We must control index in string before call something at OUString!
			// The OUString-implementation don't do it for us :-( but the result is not defined otherwise.
			if ( nPosition + 1 > aWorkText.getLength() )
			{
				// Position is out of range. Break loop!
				nPosition = STRPOS_NOTFOUND;
				nLength = 0;
			}
			else
			{
				// Else; Position is valid. Search for next variable to replace.
				nPosition = aWorkText.indexOf( m_aVarStart, nPosition );
				// Have we found any variable like "$(...)"?
				if ( nPosition != STRPOS_NOTFOUND )
				{
					// Yes; Get length of found variable. If no ")" was found - nLength must set to 0!
					nLength	= 0;
					sal_Int32 nEndPosition = aWorkText.indexOf( m_aVarEnd, nPosition );
					if ( nEndPosition != STRPOS_NOTFOUND )
						nLength = nEndPosition - nPosition + 1;
				}
			}
		}

		nPosition = aWorkText.indexOf( m_aVarStart );
		if ( nPosition == -1 )
		{
			bSubstitutionCompleted = sal_True;
			break; // All variables are substituted
		}
		else
		{
			// Check for recursion
            const sal_uInt32 nCount = aEndlessRecursiveDetector.size();
			for ( sal_uInt32 i=0; i < nCount; i++ )
			{
				if ( aEndlessRecursiveDetector[i] == aWorkText )
				{
					if ( bVarNotSubstituted )
						break; // Not all variables could be substituted!
					else
					{
						nDepth = nMaxRecursiveDepth;
						break; // Recursion detected!
					}
				}
			}

			aEndlessRecursiveDetector.push_back( aWorkText );

			// Initialize values for next
			sal_Int32 nEndPosition = aWorkText.indexOf( m_aVarEnd, nPosition );
			if ( nEndPosition != STRPOS_NOTFOUND )
				nLength = nEndPosition - nPosition + 1;
			bVarNotSubstituted = sal_False;
			++nDepth;
		}
	}

	// Fill return value with result
	if ( bSubstitutionCompleted )
	{
		// Substitution successfull!
		aResult = aWorkText;
	}
	else
	{
		// Substitution not successfull!
		if ( nDepth == nMaxRecursiveDepth )
		{
			// recursion depth reached!
			if ( bSubstRequired )
			{
				rtl::OUString aMsg( RTL_CONSTASCII_USTRINGPARAM( "Endless recursion detected. Cannot substitute variables!" ));
				throw NoSuchElementException( aMsg, (cppu::OWeakObject *)this );
			}
			else
				aResult = rText;
		}
		else
		{
			// variable in text but unknwon!
			if ( bSubstRequired )
			{
				rtl::OUString aMsg( RTL_CONSTASCII_USTRINGPARAM( "Unknown variable found!" ));
				throw NoSuchElementException( aMsg, (cppu::OWeakObject *)this );
			}
			else
				aResult = aWorkText;
		}
	}

	return aResult;
}

rtl::OUString SubstitutePathVariables::impl_reSubstituteVariables( const ::rtl::OUString& rURL )
throw ( RuntimeException )
{
    RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "framework", "Ocke.Janssen@sun.com", "SubstitutePathVariables::impl_reSubstituteVariables" );
	rtl::OUString aURL;

    INetURLObject aUrl( rURL );
    if ( !aUrl.HasError() )
        aURL = aUrl.GetMainURL( INetURLObject::NO_DECODE );
	else
	{
		// Convert a system path to a UCB compliant URL before resubstitution
		rtl::OUString aTemp;
		if ( osl::FileBase::getFileURLFromSystemPath( rURL, aTemp ) == osl::FileBase::E_None )
		{
			aTemp = ConvertOSLtoUCBURL( aTemp );
			if ( aTemp.getLength() )
				aURL = INetURLObject( aTemp ).GetMainURL( INetURLObject::NO_DECODE );
			else
				return rURL;
		}
		else
		{
			// rURL is not a valid URL nor a osl system path. Give up and return error!
			return rURL;
		}
	}

	// Due to a recursive definition this code must exchange variables with variables!
	sal_Bool		bResubstitutionCompleted	= sal_False;
	sal_Bool		bVariableFound				= sal_False;

	// Get transient predefined path variable $(work) value before starting resubstitution
	m_aPreDefVars.m_FixedVar[ PREDEFVAR_WORK ] = GetWorkVariableValue();

	while ( !bResubstitutionCompleted )
	{
		ReSubstFixedVarOrderVector::const_iterator pIterFixed;
		for ( pIterFixed = m_aReSubstFixedVarOrder.begin(); pIterFixed != m_aReSubstFixedVarOrder.end(); pIterFixed++ )
		{
			rtl::OUString aValue = m_aPreDefVars.m_FixedVar[ (sal_Int32)pIterFixed->eVariable ];
			sal_Int32 nPos = aURL.indexOf( aValue );
			if ( nPos >= 0 )
			{
				sal_Bool bMatch = sal_True;
				if ( pIterFixed->eVariable == PREDEFVAR_LANG ||
					 pIterFixed->eVariable == PREDEFVAR_LANGID ||
					 pIterFixed->eVariable == PREDEFVAR_VLANG )
				{
					// Special path variables as they can occur in the middle of a path. Only match if they
					// describe a whole directory and not only a substring of a directory!
					const sal_Unicode* pStr = aURL.getStr();

					if ( nPos > 0 )
						bMatch = ( aURL[ nPos-1 ] == '/' );

					if ( bMatch )
					{
						if ( nPos + aValue.getLength() < aURL.getLength() )
							bMatch = ( pStr[ nPos + aValue.getLength() ] == '/' );
					}
				}

				if ( bMatch )
				{
					rtl::OUStringBuffer aStrBuffer( aURL.getLength() );
					aStrBuffer.append( aURL.copy( 0, nPos ) );
					aStrBuffer.append( m_aPreDefVars.m_FixedVarNames[ (sal_Int32)pIterFixed->eVariable ] ); // Get the variable name for struct var name array!
					aStrBuffer.append( aURL.copy( nPos + aValue.getLength(), ( aURL.getLength() - ( nPos + aValue.getLength() )) ));
					aURL = aStrBuffer.makeStringAndClear();
					bVariableFound = sal_True; // Resubstitution not finished yet!
					break;
				}
			}
		}

		// This part can be iteratered more than one time as variables can contain variables again!
		ReSubstUserVarOrderVector::const_iterator pIterUser;
		for ( pIterUser = m_aReSubstUserVarOrder.begin(); pIterUser != m_aReSubstUserVarOrder.end(); pIterUser++ )
		{
			rtl::OUString aVarValue = pIterUser->aVarName;
			sal_Int32 nPos = aURL.indexOf( aVarValue );
			if ( nPos >= 0 )
			{
				rtl::OUStringBuffer aStrBuffer( aURL.getLength() );
				aStrBuffer.append( aURL.copy( 0, nPos ) );
				aStrBuffer.append( m_aVarStart );
				aStrBuffer.append( aVarValue );
				aStrBuffer.append( m_aVarEnd );
				aStrBuffer.append( aURL.copy( nPos +  aVarValue.getLength(), ( aURL.getLength() - ( nPos + aVarValue.getLength() )) ));
				aURL = aStrBuffer.makeStringAndClear();
				bVariableFound = sal_True;  // Resubstitution not finished yet!
			}
		}

		if ( !bVariableFound )
			bResubstitutionCompleted = sal_True;
		else
			bVariableFound = sal_False; // Next resubstitution
	}

	return aURL;
}

// This method support both request schemes "$("<varname>")" or "<varname>".
::rtl::OUString SubstitutePathVariables::impl_getSubstituteVariableValue( const ::rtl::OUString& rVariable )
throw ( NoSuchElementException, RuntimeException )
{
    RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "framework", "Ocke.Janssen@sun.com", "SubstitutePathVariables::impl_getSubstituteVariableValue" );
	rtl::OUString aVariable;

	sal_Int32 nPos = rVariable.indexOf( m_aVarStart );
	if ( nPos == -1 )
	{
		// Prepare variable name before hash map access
		rtl::OUStringBuffer aStrBuffer( rVariable.getLength() + m_aVarStart.getLength() + m_aVarEnd.getLength() );
		aStrBuffer.append( m_aVarStart );
		aStrBuffer.append( rVariable );
		aStrBuffer.append( m_aVarEnd );
		aVariable = aStrBuffer.makeStringAndClear();
	}

	VarNameToIndexMap::const_iterator pNTOIIter = m_aPreDefVarMap.find( ( nPos == -1 ) ? aVariable : rVariable );

	// Fixed/Predefined variable
	if ( pNTOIIter != m_aPreDefVarMap.end() )
	{
		PreDefVariable nIndex = (PreDefVariable)pNTOIIter->second;
		return m_aPreDefVars.m_FixedVar[(sal_Int32)nIndex];
	}
	else
	{
		// Prepare variable name before hash map access
		if ( nPos >= 0 )
		{
			if ( rVariable.getLength() > 3 )
				aVariable = rVariable.copy( 2, rVariable.getLength() - 3 );
			else
			{
				rtl::OUString aExceptionText( RTL_CONSTASCII_USTRINGPARAM( "Unknown variable!" ));
				throw NoSuchElementException();
			}
		}
		else
			aVariable = rVariable;

		// User defined variable
		SubstituteVariables::const_iterator pIter = m_aSubstVarMap.find( aVariable );
		if ( pIter != m_aSubstVarMap.end() )
		{
			// found!
			return pIter->second.aSubstValue;
		}

		rtl::OUString aExceptionText( RTL_CONSTASCII_USTRINGPARAM( "Unknown variable!" ));
		throw NoSuchElementException( aExceptionText, (cppu::OWeakObject *)this );
	}
}

void SubstitutePathVariables::SetPredefinedPathVariables( PredefinedPathVariables& aPreDefPathVariables )
{
    RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "framework", "Ocke.Janssen@sun.com", "SubstitutePathVariables::SetPredefinedPathVariables" );
    Any aAny;
    ::rtl::OUString aOfficePath;
    ::rtl::OUString aUserPath;
    ::rtl::OUString aTmp;
    ::rtl::OUString aTmp2;
    String          aResult;

    // Get inspath and userpath from bootstrap mechanism in every case as file URL
    ::utl::Bootstrap::PathStatus aState;
    ::rtl::OUString              sVal  ;

    aState = utl::Bootstrap::locateBaseInstallation( sVal );
    if( aState==::utl::Bootstrap::PATH_EXISTS ) {
        aPreDefPathVariables.m_FixedVar[ PREDEFVAR_INSTPATH ] = ConvertOSLtoUCBURL( sVal );
    }
    else {
        LOG_ERROR( "SubstitutePathVariables::SetPredefinedPathVariables", "Bootstrap code has no value for instpath!");
    }

    aState = utl::Bootstrap::locateUserData( sVal );
    if( aState == ::utl::Bootstrap::PATH_EXISTS ) {
        aPreDefPathVariables.m_FixedVar[ PREDEFVAR_USERPATH ] = ConvertOSLtoUCBURL( sVal );
    }
    else {
        LOG_ERROR( "SubstitutePathVariables::SetPredefinedPathVariables", "Bootstrap code has no value for userpath");
    }

	// Set $(inst), $(instpath), $(insturl)
    aPreDefPathVariables.m_FixedVar[ PREDEFVAR_INSTURL ]	= aPreDefPathVariables.m_FixedVar[ PREDEFVAR_INSTPATH ];
	aPreDefPathVariables.m_FixedVar[ PREDEFVAR_INST ]		= aPreDefPathVariables.m_FixedVar[ PREDEFVAR_INSTPATH ];
    // --> PB 2004-10-27 #i32656# - new variable of hierachy service
    aPreDefPathVariables.m_FixedVar[ PREDEFVAR_BASEINSTURL ]= aPreDefPathVariables.m_FixedVar[ PREDEFVAR_INSTPATH ];
    // <--

	// Set $(user), $(userpath), $(userurl)
	aPreDefPathVariables.m_FixedVar[ PREDEFVAR_USERURL ]	= aPreDefPathVariables.m_FixedVar[ PREDEFVAR_USERPATH ];
	aPreDefPathVariables.m_FixedVar[ PREDEFVAR_USER ]		= aPreDefPathVariables.m_FixedVar[ PREDEFVAR_USERPATH ];
    // --> PB 2004-11-11 #i32656# - new variable of hierachy service
    aPreDefPathVariables.m_FixedVar[ PREDEFVAR_USERDATAURL ]= aPreDefPathVariables.m_FixedVar[ PREDEFVAR_USERPATH ];
    // <--

	// Detect the program directory
	// Set $(prog), $(progpath), $(progurl)
    INetURLObject aProgObj(
        aPreDefPathVariables.m_FixedVar[ PREDEFVAR_INSTPATH ] );
    if ( !aProgObj.HasError() &&
         aProgObj.insertName(
             rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("program")) ) )
    {
		aPreDefPathVariables.m_FixedVar[ PREDEFVAR_PROGPATH ]	= aProgObj.GetMainURL(INetURLObject::NO_DECODE);
		aPreDefPathVariables.m_FixedVar[ PREDEFVAR_PROGURL ]	= aPreDefPathVariables.m_FixedVar[ PREDEFVAR_PROGPATH ];
		aPreDefPathVariables.m_FixedVar[ PREDEFVAR_PROG ]		= aPreDefPathVariables.m_FixedVar[ PREDEFVAR_PROGPATH ];
    }

	// Detect the language type of the current office
	aPreDefPathVariables.m_eLanguageType = LANGUAGE_ENGLISH_US;
	rtl::OUString aLocaleStr;
	if ( utl::ConfigManager::GetConfigManager()->GetDirectConfigProperty( utl::ConfigManager::LOCALE ) >>= aLocaleStr )
		aPreDefPathVariables.m_eLanguageType = MsLangId::convertIsoStringToLanguage( aLocaleStr );
	else
	{
		LOG_ERROR( "SubstitutePathVariables::SetPredefinedPathVariables", "Wrong Any type for language!" );
	}

	// Set $(lang)
	aPreDefPathVariables.m_FixedVar[ PREDEFVAR_LANG ] = ConvertOSLtoUCBURL(
		rtl::OUString::createFromAscii( ResMgr::GetLang( aPreDefPathVariables.m_eLanguageType, 0 ) ));
	// Set $(vlang)
	aPreDefPathVariables.m_FixedVar[ PREDEFVAR_VLANG ] = aLocaleStr;

	// Set $(langid)
	aPreDefPathVariables.m_FixedVar[ PREDEFVAR_LANGID ] = rtl::OUString::valueOf( (sal_Int32)aPreDefPathVariables.m_eLanguageType );

	// Set the other pre defined path variables
	// Set $(work)
	aPreDefPathVariables.m_FixedVar[ PREDEFVAR_WORK ] = GetWorkVariableValue();
	aPreDefPathVariables.m_FixedVar[ PREDEFVAR_HOME ] = GetHomeVariableValue();

	// Set $(workdirurl) this is the value of the path PATH_WORK which doesn't make sense
	// anymore because the path settings service has this value! It can deliver this value more
	// quickly than the substitution service!
	aPreDefPathVariables.m_FixedVar[ PREDEFVAR_WORKDIRURL ] = GetWorkPath();

	// Set $(path) variable
	aPreDefPathVariables.m_FixedVar[ PREDEFVAR_PATH ] = GetPathVariableValue();

	// Set $(temp)
	osl::FileBase::getTempDirURL( aTmp );
	aPreDefPathVariables.m_FixedVar[ PREDEFVAR_TEMP ] = ConvertOSLtoUCBURL( aTmp );

    aPreDefPathVariables.m_FixedVar[PREDEFVAR_BRANDBASEURL] = rtl::OUString(
        RTL_CONSTASCII_USTRINGPARAM("$BRAND_BASE_DIR"));
    rtl::Bootstrap::expandMacros(
        aPreDefPathVariables.m_FixedVar[PREDEFVAR_BRANDBASEURL]);
}

} // namespace framework
